#include "pch.h"
#include "FreeType.h"
#include <vector>
using namespace CnyeCPlus::FType;

int GlyphSpans::RGBA(unsigned char** buf,PixelRGBA foreground,PixelRGBA background){
	// Get some metrics of our image.
	int imgWidth = this->Width(),
		imgHeight = this->Height(),
		imgSize = imgWidth * imgHeight;

	// Allocate data for our image and clear it out to transparent.
	int buflen=sizeof(PixelRGBA) * imgSize;
	PixelRGBA *pxl = new PixelRGBA[imgSize];
	memset(pxl, 0, buflen);

	// Loop over the outline spans and just draw them into the
	// image.
	/*for (Spans::iterator s = this->OSpans.begin();
	s != this->OSpans.end(); ++s){
	for (int w = 0; w < s->width; ++w)
	{
	pxl[(int)((imgHeight - 1 - (s->y - ymin)) * imgWidth+ s->x - xmin + w)] =
	PixelRGBA(background.r, background.g, background.b,s->coverage);
	}
	}*/

	// Then loop over the regular glyph spans and blend them into
	// the image.
	for (Spans::iterator s = this->NSpans.begin();
		s != this->NSpans.end(); ++s){
			for (int w = 0; w < s->width; ++w)
			{
				//PixelRGBA &dst =
				pxl[(int)((imgHeight - 1 - (s->y - ymin)) * imgWidth+ s->x - xmin + w)]=
					PixelRGBA(foreground.r, foreground.g, foreground.b,
					s->coverage);
				/*dst.r = (int)(dst.r + ((src.r - dst.r) * src.a) / 255.0f);
				dst.g = (int)(dst.g + ((src.g - dst.g) * src.a) / 255.0f);
				dst.b = (int)(dst.b + ((src.b - dst.b) * src.a) / 255.0f);
				dst.a = MIN(255, dst.a + src.a);*/
			}
	}
	*buf=(unsigned char*)pxl;
	return buflen;
}
void FreeType::RasterCallback(const int y,const int count,const FT_Span * const spans,void * const user) 
{
	Spans *sptr = (Spans *)user;
	for (int i = 0; i < count; ++i){
		Span s(spans[i].x, y, spans[i].len, spans[i].coverage);
		sptr->gs->Include(s.x, s.y);
		sptr->gs->Include(s.x + s.width - 1, s.y);
		sptr->push_back(s);
	}
}
void FreeType::RenderSpans(FT_Library &library,FT_Outline * const outline,Spans *spans) 
{
	FT_Raster_Params params;
	memset(&params, 0, sizeof(params));
	params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
	params.gray_spans = RasterCallback;
	params.user = spans;
	FT_Outline_Render(library, outline, &params);
}

FreeType::FreeType(const Platform::Array<unsigned char>^ fontbuf)
{
	FT_Init_FreeType(&library);
	/*this->Background=Windows::UI::ColorHelper::FromArgb(255,255,255,255);
	this->Foreground=Windows::UI::ColorHelper::FromArgb(255,0,0,0);*/
	this->Init(fontbuf->Data,fontbuf->Length);
	this->charSize=0;
}
FreeType::FreeType(unsigned char *fbuf,unsigned int fbufSize){
	FT_Init_FreeType(&library);
	this->Init(fbuf,fbufSize);
	this->charSize=0;
}

FreeType::~FreeType(void)
{
	for(std::vector<GlyphSpans*>::iterator it=this->LoadedGlyph.begin();it!=this->LoadedGlyph.end();it++){
		GlyphSpans* gs=*it;
		delete gs;
	}
	this->FreeBitmap();
	this->LoadedGlyph.clear();
	FT_Done_Face(face);
	FT_Done_FreeType(library);
}
void FreeType::SetCharSize(
	float char_width,float char_height,unsigned int horz_resolution,unsigned int vert_resolution){
		FT_Set_Char_Size(face, (char_width*2*2*2*2*2*2), (char_height*2*2*2*2*2*2), horz_resolution, vert_resolution);
		if(abs(char_width-this->charSize)>=0.2){
			//this->FreeBitmap();
		}
}
Windows::Foundation::Size FreeType::GenerateGlyph(wchar_t c,float outlineWidth){
	GlyphSpans* gs=this->LoadGlyph(c,outlineWidth);
	if(gs!=nullptr){
		return Windows::Foundation::Size(gs->Width(),gs->Height());
	}else{
		return Windows::Foundation::Size(0,0);
	}
}
//Platform::Array<byte>^ FreeType::CharGlyph(wchar_t c){
//	Platform::Array<byte>^ bys=nullptr;
//
//	/*byte *buf=0;
//	int len=this->CharGlyph(c,&buf,3.0f);
//	if(len>0){
//	bys=ref new Platform::Array<byte>(buf,len);
//	delete buf;
//	}*/
//	return bys;
//}
void FreeType::Init(unsigned char *fbuf,unsigned int fbufSize){
	FT_New_Memory_Face(library, fbuf, fbufSize, 0, &face);
}
GlyphSpans* FreeType::LoadGlyph(wchar_t c,float outlineWidth){
	GlyphSpans* gs=this->GetGlyph(c);
	if(gs!=0){
		return gs;
	}
	gs=new GlyphSpans();
	gs->c=c;
	// Load the glyph we are looking for.
	FT_UInt gindex = FT_Get_Char_Index(face, c);
	if (FT_Load_Glyph(face, gindex, FT_LOAD_NO_BITMAP) == 0)
	{
		// Need an outline for this to work.
		if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
		{
			// Render the basic glyph to a span list.
			RenderSpans(library, &face->glyph->outline, &gs->NSpans);

			//// Set up a stroker.
			//FT_Stroker stroker;
			//FT_Stroker_New(library, &stroker);
			//FT_Stroker_Set(stroker,
			//	(int)(outlineWidth * 64),
			//	FT_STROKER_LINECAP_ROUND,
			//	FT_STROKER_LINEJOIN_ROUND,
			//	0);

			//FT_Glyph glyph;
			//if (FT_Get_Glyph(face->glyph, &glyph) == 0)
			//{
			//	FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
			//	// Again, this needs to be an outline to work.
			//	if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
			//	{
			//		// Render the outline spans to the span list
			//		FT_Outline *o =
			//			&reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
			//		RenderSpans(library, o, &gs->OSpans);
			//	}

			//	// Clean up afterwards.
			//	FT_Stroker_Done(stroker);
			//	FT_Done_Glyph(glyph);
			//}
		}
	}
	// Now we need to put it all together.
	if (gs->NSpans.empty())
	{
		delete gs;
		return 0;
	}else{
		this->AddGlyph(gs);
		return gs;
	}
}
void FreeType::AddGlyph(GlyphSpans* gs){
	this->FreeGlyph(gs->c);
	if(this->LoadedGlyph.size()>100){
		std::vector<GlyphSpans*>::iterator it=this->LoadedGlyph.begin();
		GlyphSpans* gs=*it;
		this->LoadedGlyph.erase(it);
		delete gs;
	}
	this->LoadedGlyph.push_back(gs);
}
GlyphSpans* FreeType::GetGlyph(wchar_t c){
	size_t len=this->LoadedGlyph.size();
	for(unsigned int i=0;i<len;i++){
		if(c==this->LoadedGlyph[i]->c){
			return this->LoadedGlyph[i];
		}
	}
	return 0;
}
void FreeType::FreeGlyph(wchar_t c){
	for(std::vector<GlyphSpans*>::iterator it=this->LoadedGlyph.begin();it!=this->LoadedGlyph.end();it++){
		GlyphSpans* gs=*it;
		if(gs->c==c){
			this->LoadedGlyph.erase(it);
			delete gs;
			break;
		}
	}
}

ID2D1Bitmap1* FreeType::CreateBitmap(
	ID2D1DeviceContext* context,IWICImagingFactory* factory,
	wchar_t c,Windows::UI::Color tcolor,Windows::UI::Color bcolor){
		if(this->Bitmaps.find(c)!=this->Bitmaps.end()){
			return this->Bitmaps[c];
		}
		//
		util::Timer::SharedTimer->Beg();
		GlyphSpans *gs=this->LoadGlyph(c);
		util::Timer::SharedTimer->Delay();
		unsigned char* buf;

		int blen=gs->RGBA(&buf,PixelRGBA(tcolor.R,tcolor.G,tcolor.B,tcolor.A),PixelRGBA(bcolor.R,bcolor.G,bcolor.B,bcolor.A));
		if(blen<1){
			return nullptr;
		}
		int imgw=gs->Width(),imgh=gs->Height();
		int lwidth=imgw*sizeof(PixelRGBA);
		IWICBitmap *wicimg;
		factory->CreateBitmapFromMemory(
			imgw,imgh,GUID_WICPixelFormat32bppPRGBA,lwidth,lwidth*imgh,buf,&wicimg);
		ID2D1Bitmap1 *bitimg=nullptr;
		context->CreateBitmapFromWicBitmap(wicimg,&bitimg);
		DX::SafeRelease(&wicimg);
		delete buf;
		if(bitimg!=nullptr){
			this->Bitmaps[c]=bitimg;
		}
		return bitimg;
}
void FreeType::FreeBitmap(){
	std::map<wchar_t,ID2D1Bitmap1*>::iterator beg,end;
	beg=this->Bitmaps.begin(),end=this->Bitmaps.end();
	for(;beg!=end;beg++){
		ID2D1Bitmap1* bitmap=beg->second;
		DX::SafeRelease(&bitmap);
	}
	this->Bitmaps.clear();
}
